package io.iamting93.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author boting.guo
 * @date 2021/12/6 18:48
 */

@Slf4j
@RefreshScope
@Component
public class RegisterFilter implements GlobalFilter, Ordered {

    @Value("${ip.times}")
    private Integer registerTimes;

    @Value("${ip.millis}")
    private Integer registerMillis;

    private Map<String, LinkedList<Long>> record = new ConcurrentHashMap<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        String requestUrl = exchange.getRequest().getPath().pathWithinApplication().value();
        if (requestUrl.contains("/register")) {

            // 滑动窗口实现ip防爆刷
            String clientIP = Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostString();

            if (clientIP != null) {
                if (!record.containsKey(clientIP)) {
                    record.put(clientIP, new LinkedList<>());
                }

                synchronized (record.get(clientIP)) {
                    LinkedList<Long> requestRecords = record.get(clientIP);
                    long now = System.currentTimeMillis();
                    Iterator<Long> iterator = requestRecords.iterator();
                    while (iterator.hasNext()) {
                        Long cur = iterator.next();
                        if (cur + registerMillis < now) {
                            iterator.remove();
                        } else {
                            break;
                        }
                    }

                    requestRecords.addLast(now);

                    log.info("ip: " + clientIP + "在" + (registerMillis / 1000) + "秒内请求了" + requestRecords.size() + "次");

                    if (requestRecords.size() > registerTimes) {
                        ServerHttpResponse response = exchange.getResponse();
                        response.setStatusCode(HttpStatus.SEE_OTHER); // 状态码
                        String data = "频繁进行注册，请求已被拒绝";
                        DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
                        return response.writeWith(Mono.just(wrap));
                    }
                }
            }
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
